package com.denlaku.longan.filter;

import com.denlaku.longan.Headers;
import com.denlaku.longan.core.Current;
import com.denlaku.longan.core.RequestContext;
import com.denlaku.longan.core.RequestContextManager;
import com.denlaku.longan.core.TenantCacheManager;
import com.denlaku.longan.core.UserTenantCacheManager;
import com.denlaku.longan.vo.Tenant;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 * @author tianx
 */
public class TenantRequestFilter extends HttpFilter {
    private static final String IMAGE_RESOURCE_PATH = "/image/resource/";
    private static final String CUSTOM_PAGE_PATH = "/page/";
    private static final String API_PATH = "/api/";

    private final Map<String, Function<HttpServletRequest, Long>> FUNC_MAP = new LinkedHashMap<>();

    {
        FUNC_MAP.put(API_PATH, this::parseTenantIdFromHeader);
        FUNC_MAP.put(IMAGE_RESOURCE_PATH, this::parseTenantIdFromResourceUrl);
        FUNC_MAP.put(CUSTOM_PAGE_PATH, this::parseTenantIdCustomPageUrl);
    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String contextPath = request.getContextPath();
        String requestUri = request.getRequestURI();
        String api = requestUri.substring(contextPath.length());
        for (Map.Entry<String, Function<HttpServletRequest, Long>> ent : FUNC_MAP.entrySet()) {
            String key = ent.getKey();
            if (!api.startsWith(key)) {
                continue;
            }
            Function<HttpServletRequest, Long> func = ent.getValue();
            Long tenantId = func.apply(request);
            if (validateTenant(tenantId)) {
                RequestContext current = RequestContextManager.getCurrent();
                current.setTenant(TenantCacheManager.getTenant(tenantId));
                current.setHeader(Headers.X_TENANT_ID, tenantId);
                break;
            } else {
                response.sendError(HttpStatus.NOT_FOUND.value());
                return;
            }
        }

        super.doFilter(request, response, chain);
    }

    private boolean validateTenant(Long tenantId) {
        if (tenantId == null) {
            return false;
        }
        Tenant tenant = TenantCacheManager.getTenant(tenantId);
        if (tenant == null) {
            return false;
        }
        Long userId = Current.getUserId();
        if (Objects.equals(tenant.getOwner(), userId)) {
            return true;
        }
        return UserTenantCacheManager.exists(tenantId, userId);
    }

    private Long parseTenantIdFromHeader(HttpServletRequest request) {
        String tenantId = request.getHeader(Headers.X_TENANT_ID);
        if (StringUtils.isNotEmpty(tenantId)) {
            return Long.parseLong(tenantId);
        }
        return null;
    }

    private Long parseTenantIdFromResourceUrl(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        String requestUri = request.getRequestURI();
        String api = requestUri.substring(contextPath.length());
        String path = api.substring(IMAGE_RESOURCE_PATH.length());
        int idx = path.indexOf("/", 1);
        String tenantId = path.substring(0, idx);
        if (StringUtils.isNotEmpty(tenantId)) {
            return Long.parseLong(tenantId);
        }
        return null;
    }

    private Long parseTenantIdCustomPageUrl(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        String requestUri = request.getRequestURI();
        String api = requestUri.substring(contextPath.length());
        String path = api.substring(CUSTOM_PAGE_PATH.length());
        int index = path.indexOf("/", 1);
        String tenantId = path.substring(0, index);
        if (StringUtils.isNotEmpty(tenantId)) {
            return Long.parseLong(tenantId);
        }
        return null;
    }

}
